package org.jiuling.local.tools.util;

import org.jiuling.local.tools.convert.DateConvert;
import org.jiuling.local.tools.convert.StringConvert;
import org.jiuling.local.tools.enums.DateTimeFormatEnum;
import org.jiuling.local.tools.exception.LocalToolsException;
import org.jiuling.local.tools.verify.Assertions;
import org.jiuling.local.tools.verify.BooleanVerify;
import org.jiuling.local.tools.verify.ObjectVerify;

import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.JarURLConnection;
import java.net.URL;
import java.net.URLDecoder;
import java.util.Date;
import java.util.Enumeration;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 类扩展工具
 * @author 紫灵
 * @date 2023年02月15日 15时11分33秒
 */
@Deprecated
public class ClassUtil {

    /** 私有构造方法，禁止创建对象 */
    private ClassUtil(){}

    /**
     * 获取属性值
     * @param object 对象
     * @param fieldName 属性名称
     * @param forceAcquisition 是否强制获取
     * @return 对应值
     * @throws LocalToolsException 本地工具异常
     */
    public static Object getFieldValue(Object object,String fieldName,Boolean forceAcquisition) throws LocalToolsException {
        Class<?> clazz = object.getClass();
        try {
            Field field = clazz.getDeclaredField(fieldName);
            // 判断是否为私有属性
            if(!field.canAccess(object)){
                // 判断是否强制获取，如果为强制获取则将私有属性改为共有属性后强制获取
                if(BooleanVerify.isTrue(forceAcquisition)){
                    field.setAccessible(true);
                    Object fieldValue = field.get(object);
                    field.setAccessible(false);
                    return String.valueOf(fieldValue);
                }
                // 如果不强制获取则通过get或者is方法获取
                Class<?> fieldType = field.getType();
                String prefixes = "get";
                if(fieldType.equals(boolean.class) || fieldType.equals(Boolean.class)){
                    prefixes = "is";
                }
                Method getMethod = getMethodByFieldName(clazz,fieldName,prefixes);
                Assertions.objectIsNotNull(fieldName+"未实现"+prefixes+"方法！",getMethod);
                return getMethod.invoke(object);
            }
            // 如果为共有方法则直接获取
            return field.get(object);
        } catch (NoSuchFieldException | IllegalAccessException | InvocationTargetException e) {
            throw new LocalToolsException(e.getLocalizedMessage());
        }
    }

    /**
     * 获取属性值
     * @param object 对象
     * @param fieldName 属性名称
     * @return 对应值
     * @throws LocalToolsException 本地工具异常
     */
    public static Object getFieldValue(Object object,String fieldName) throws LocalToolsException {
        return getFieldValue(object,fieldName,null);
    }

    /**
     * 通过属性名称获取方法
     * @param clazz 类
     * @param fieldName 属性名称
     * @param prefixes 前缀
     * @return 方法对象
     */
    public static Method getMethodByFieldName(Class<?> clazz, String fieldName, String prefixes, Class<?>... parameterTypes) throws LocalToolsException {
        try {
            return clazz.getMethod(prefixes+StringConvert.initialsToUpperCase(fieldName),parameterTypes);
        } catch (NoSuchMethodException e) {
            throw new LocalToolsException(clazz.getName()+" 中属性 "+fieldName+" 没有实现 "+prefixes+" 方法！");
        }
    }

    /**
     * 设置属性值
     * @param object 对象
     * @param field 属性
     * @param value 值
     * @param mandatorySetting 是否强制设置
     */
    public static void setFieldValue(Object object,Field field,Object value,Boolean mandatorySetting) throws LocalToolsException {
        if(!ObjectVerify.isNull(value)) {
            // 判断是否为时间类型
            Class<?> fieldType = field.getType();
            if(fieldType.equals(Date.class)){
                // TODO 获取注解是否有时间格式，如果没有则使用默认时间格式
                value = DateConvert.stringToDate(String.valueOf(value), DateTimeFormatEnum.yyyy_MM_dd_HH_mm_ss);
            }
            try {
                // 判断属性是否为私有属性
                if(!field.canAccess(object)){
                    // 判断是否强制设置
                    if(BooleanVerify.isTrue(mandatorySetting)){
                        field.setAccessible(true);
                        field.set(object,value);
                        field.setAccessible(false);
                    }else {
                        // 获取set方法
                        String fieldName = field.getName();
                        Method setMethod = getMethodByFieldName(object.getClass(), field.getName(), "set",fieldType);
                        Assertions.objectIsNotNull(fieldName+"未实现set方法！",setMethod);
                        setMethod.invoke(object,value);
                    }
                }else {
                    field.set(object,value);
                }
            } catch (IllegalAccessException | InvocationTargetException e) {
                throw new LocalToolsException(e.getLocalizedMessage());
            }
        }
    }

    /**
     * 设置属性值
     * @param object 对象
     * @param field 属性
     * @param value 值
     */
    public static void setFieldValue(Object object,Field field,Object value) throws LocalToolsException {
        setFieldValue(object, field, value,null);
    }

    /**
     * 迭代
     */
    private static boolean iteration = false;

    /**
     * 扫描包
     * @param packs 包
     * @return 包列表
     */
    public static Set<String> scanPackage(String... packs){
        Set<String> packageDis = new LinkedHashSet<>();
        for (String pack : packs){
            scanPackage(pack,packageDis);
        }
        return packageDis;
    }

    /**
     * 扫描包
     * @param pack 包
     * @param packageSet 包列表
     * @return 包列表
     */
    private static Set<String> scanPackage(String pack,Set<String> packageSet){
        // 通过正则获取带星号的包的前缀贺后缀
        Matcher matcher = Pattern.compile("(.*?)\\.\\*(.*)").matcher(pack);
        if (matcher.matches()) {
            // 当星号后的值为空的时候则进行迭代
            if (matcher.group(2) == null || matcher.group(2).equals("")){
                iteration = true;
            }
            for(String fileName : getPackageFile(matcher.group(1),false)){
                scanPackage(matcher.group(1)+"."+fileName+matcher.group(2),packageSet);
            }
        }else {
            if(!getPackageFile(pack,true).isEmpty()){
                packageSet.add(pack);
            }
        }
        if(iteration){
            for(String fileName : getPackageFile(pack,false)){
                scanPackage(pack+"."+fileName,packageSet);
            }
        }
        return packageSet;
    }

    /**
     * 获取jar包中包路径中文件
     * @param url 地址
     * @param type 获取类型 true表示文件 false表示文件夹
     * @param packageDirName 包路径名
     * @return 有效文件列表
     */
    private static Set<String> getJarPackageFile(URL url, Boolean type, String packageDirName) throws IOException {
        Set<String> packageFile = new LinkedHashSet<>();
        // 从此jar包 得到一个枚举类
        Enumeration<JarEntry> entries = ((JarURLConnection) url.openConnection()).getJarFile().entries();
        // 同样的进行循环迭代
        while (entries.hasMoreElements()){
            // 获取jar里的一个实体 可以是目录 和一些jar包里的其他文件 如META-INF等文件
            JarEntry entry = entries.nextElement();
            String name = entry.getName();
            // 如果是以/开头的
            if (name.charAt(0) == '/') {
                // 获取后面的字符串
                name = name.substring(1);
            }
            if(type == null){
                // 如果前半部分和定义的包名相同
                if (!entry.isDirectory() && name.startsWith(packageDirName)){
                    Matcher matcher = Pattern.compile(packageDirName + ".*?/(.*?)").matcher(name);
                    if (matcher.matches()) {
                        Matcher fileMatcher = Pattern.compile("/").matcher(matcher.group(1));
                        if(!fileMatcher.find()){
                            packageFile.add(matcher.group(1));
                        }
                    }
                }else {
                    // 如果前半部分和定义的包名相同
                    if (name.startsWith(packageDirName)) {
                        Matcher matcher = Pattern.compile(packageDirName + "/(.*?)/.*").matcher(name);
                        if (matcher.matches()) {
                            packageFile.add(matcher.group(1));
                        }
                    }
                }
            }else if(Boolean.TRUE.equals(type) && !entry.isDirectory()){
                if (name.startsWith(packageDirName)) {
                    Matcher matcher = Pattern.compile(packageDirName + ".*?/(.*?)").matcher(name);
                    if (matcher.matches()) {
                        Matcher fileMatcher = Pattern.compile("/").matcher(matcher.group(1));
                        if(!fileMatcher.find()){
                            packageFile.add(matcher.group(1));
                        }
                    }
                }
            }else {
                if (entry.isDirectory() && name.startsWith(packageDirName)) {
                    // 如果前半部分和定义的包名相同
                    Matcher matcher = Pattern.compile(packageDirName + "/(.*?)/.*").matcher(name);
                    if (matcher.matches()) {
                        packageFile.add(matcher.group(1));
                    }
                }
            }
        }
        return packageFile;
    }

    /**
     * 获取文件或者文件夹中包路径中文件
     * @param url 地址
     * @param type 获取类型 true表示文件 false表示文件夹
     * @return 有效文件列表
     */
    private static Set<String> getFilePackageFile(URL url,Boolean type) throws UnsupportedEncodingException {
        Set<String> packageFile = new LinkedHashSet<>();
        // 获取包的物理路径
        String filePath = URLDecoder.decode(url.getFile(), "UTF-8");
        // 获取此包的目录 建立一个File
        File dir = new File(filePath);
        // 如果不存在或者文件不是目录
        if(!dir.exists() || !dir.isDirectory()){
            return packageFile;
        }
        // 获取目录中文件列表
        File[] dirfiles = dir.listFiles();
        if (dirfiles == null || dirfiles.length <= 0){
            return packageFile;
        }
        for (File file : dirfiles){
            if(type == null){
                packageFile.add(file.getName());
            }else if (Boolean.TRUE.equals(type)){
                if (!file.isDirectory()) {
                    packageFile.add(file.getName());
                }
            }else {
                if (file.isDirectory()) {
                    packageFile.add(file.getName());
                }
            }
        }
        return packageFile;
    }

    /**
     * 获取包路径中文件
     * @param packPath 包路径
     * @param type 获取类型 true表示文件 false表示文件夹
     * @return 有效文件列表（不包含空目录）
     */
    public static Set<String> getPackageFile(String packPath,Boolean type){
        Set<String> packageFile = new LinkedHashSet<>();
        String packageDirName = packPath.replace('.', '/');
        try {
            Enumeration<URL> dirs = Thread.currentThread().getContextClassLoader().getResources(packageDirName);
            // 迭代包路径
            while (dirs.hasMoreElements()) {
                // 获取下一个元素
                URL url = dirs.nextElement();
                // 得到协议的名称
                String protocol = url.getProtocol();
                if("file".equals(protocol)){
                    Set<String> filePackageFile = getFilePackageFile(url,type);
                    packageFile.addAll(filePackageFile);
                }else if ("jar".equals(protocol)){
                    packageFile.addAll(getJarPackageFile( url, type, packageDirName));
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return packageFile;
    }

    /**
     * 扫描Class
     * @param packs 包
     * @return class列表
     */
    public static Set<Class<?>> scanClases(String... packs){
        Set<Class<?>> classes = new LinkedHashSet<>();
        for (String pack : packs){
            Set<String> packages = new LinkedHashSet<>();
            for (String packName : scanPackage(pack,packages)){
                scanClases(packName,classes);
            }
        }
        return classes;
    }

    /**
     * 扫描class
     * @param pack 包
     * @return class列表
     */
    private static Set<Class<?>> scanClases(String pack,Set<Class<?>> classes){
        for (String className : getPackageFile(pack,true)){
            className = className.substring(0,className.length() - 6);
            try {
                classes.add(Class.forName(pack + '.' + className));
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
        return classes;
    }

}
